#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import datetime
import json
import os

from common.common import exter_attack, get_host_sn
from common.common_models import LogDetail
from common.const import RepositoryDataTypeEnum, SysData, DBLogLevel
from common.util.check_utils import is_valid_uuid
from exchange import log
from exchange.commons.common import check_sh_cmd_param
from exchange.commons.const import ExchangeJobType, ExchangeJobInfo, ParamConstant, PERCENT
from exchange.commons.exchange_exception import ExchangeErrCodeException
from exchange.restore.restore_param_parser import RestoreParamParser



class ExchangeParamParse:

    @staticmethod
    def check_path(path_param):
        if not os.path.realpath(path_param).startswith('/mnt/databackup/'):
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        return True

    @staticmethod
    @exter_attack
    def get_data_reps(param):
        data_reps = []
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.DATA_REPOSITORY:
                if len(rep.get("path", [])) > 0:
                    data_reps.append(rep)
        if len(data_reps) == 0:
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        return data_reps

    @staticmethod
    @exter_attack
    def get_meta_rep(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                if len(rep.get("path", [])) > 0:
                    return rep
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_cache_rep(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.CACHE_REPOSITORY:
                if len(rep.get("path", [])) > 0:
                    return rep
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_cache_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.CACHE_REPOSITORY:
                cache_hub = rep.get("path", [""])[0]
                return cache_hub
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_meta_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                meta_hub_volume = rep.get("path", [""])[0]
                return meta_hub_volume
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_first_data_meta_root_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                meta_hub_volume = rep.get("path", [""])[0]
                return ExchangeParamParse.convert_hub_path(meta_hub_volume)
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_last_data_meta_root_path(param):
        # 日志备份场景，取meta仓
        repositories = param.get("job", {}).get("repositories")
        repositories.reverse()
        for rep in repositories:
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                meta_hub_volume = rep.get("path", [""])[0]
                return ExchangeParamParse.convert_hub_path(meta_hub_volume)
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    def convert_timestamp_to_datetime(s_timestamp):
        try:
            s_timestamp = int(s_timestamp)
        except ValueError as ex:
            raise Exception("Timestamp parameter is invalid") from ex
        return datetime.datetime.fromtimestamp(s_timestamp).strftime(ParamConstant.RECOVERY_TARGET_TIME_FORMATTER)

    @staticmethod
    @exter_attack
    def convert_hub_path(path):
        # 拼接/rep/{agent_id}路径并初始化
        new_path = path.replace("/", "\\")
        return new_path

    @staticmethod
    @exter_attack
    def get_sqlite_meta_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                meta_hub_volume = rep.get("path", [""])[0]
                return meta_hub_volume
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_data_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.DATA_REPOSITORY:
                data_hub_volume = rep.get("path")[0]
                return data_hub_volume
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_log_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.LOG_REPOSITORY:
                log_hub = rep.get("path")[0]
                return log_hub
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))
    
    @staticmethod
    @exter_attack
    def get_available_capacity_threshold(json_param: dict):
        try:
            return int(json_param.get("job").get("extendInfo").get("available_capacity_threshold", PERCENT))
        except Exception as err:
            err_msg = f"Failed get available_capacity_threshold, err: {err}."
            log.error(err_msg)
            raise Exception(err_msg) from err
        

    @staticmethod
    @exter_attack
    def get_single_database_info(param):
        db_uuid = param.get("job", {}).get("protectObject", {}).get("extendInfo", {}).get("db_uuid")
        edb_path = param.get("job", {}).get("protectObject", {}).get("extendInfo", {}).get("edb_path")
        log_path = param.get("job", {}).get("protectObject", {}).get("extendInfo", {}).get("log_path")
        db_name = param.get("job").get("protectObject").get("name")
        agent_id = get_host_sn()
        temp = {"edb_path": edb_path, "log_path": log_path}
        resource = {
            "name": db_name, "type": "Database", "agentId": agent_id, "uuid": db_uuid, "extendInfo": temp
        }
        resource_list = []
        # 盘符列表
        drive_letter = [edb_path[0], log_path[0]]
        guids = [db_uuid]
        resource_list.append(resource)
        drive_letter = list(set(drive_letter))
        return resource_list, drive_letter, guids

    @staticmethod
    @exter_attack
    def get_copy_id(param):
        copy_list = param.get("job", {}).get("copy")
        if len(copy_list) != 0:
            if is_valid_uuid(copy_list[0].get("id", None)):
                copy_id = copy_list[0].get("id", None)
                return copy_id
        raise ExchangeErrCodeException(log_detail=LogDetail(
            logDetail="",
            logDetailParam=[""],
            logLevel=DBLogLevel.ERROR))

    @staticmethod
    @exter_attack
    def get_type(param):
        backup_type = param.get("job", {}).get("jobParam", {}).get("backupType")
        log.info(f"Get backup_type: {backup_type}")
        return backup_type

    @staticmethod
    @exter_attack
    def get_job_id(param):
        job_id = param.get("job", {}).get("jobId")
        if not check_sh_cmd_param(job_id):
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        return job_id

    @staticmethod
    @exter_attack
    def get_sub_job_id(param):
        sub_job_id = param.get("subJob", {}).get("subJobId")
        if not sub_job_id:
            return sub_job_id
        if not check_sh_cmd_param(sub_job_id):
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        return sub_job_id

    @staticmethod
    @exter_attack
    def get_backup_result(param):
        return param.get("backupJobResult")

    @staticmethod
    @exter_attack
    def get_std_in_variable(str_env_variable: str):
        env_variable = ''
        input_dict = json.loads(SysData.SYS_STDIN)
        if input_dict.get(str_env_variable):
            env_variable = input_dict.get(str_env_variable)
        return env_variable

    @staticmethod
    @exter_attack
    def get_job_type(param):
        backup_type = ExchangeParamParse.get_type(param)
        job_type = ExchangeJobType.job_type_dict.get(backup_type)
        log.info(f"Get job type {job_type}")
        return job_type

    @staticmethod
    @exter_attack
    def get_restore_job_info(pid, job_id, sub_job_id, param):
        cache_path = ExchangeParamParse.get_single_type_repo_path_from_restore_param(param,
                     RepositoryDataTypeEnum.CACHE_REPOSITORY)
        data_reps = RestoreParamParser.get_exchange_database_copy_repo(param)
        job_info = ExchangeJobInfo(pid=pid,
                                   job_id=job_id,
                                   sub_job_id=sub_job_id,
                                   cache_path=cache_path,
                                   data_reps=data_reps)
        return job_info

    @staticmethod
    @exter_attack
    def get_restore_node_ip(param):
        node_id = get_host_sn()
        nodes = param.get("job").get("targetEnv").get("nodes")
        node_ip = ""
        for node in nodes:
            if node['id'] == node_id:
                node_ip = node['endpoint']
                return node_ip
        return node_ip

    @staticmethod
    @exter_attack
    def get_single_type_repo_path_from_restore_param(param, target_type):
        copy_info = param.get("job", {}).get("copies", [])[0]
        repositories = copy_info.get("repositories")
        if not repositories or len(repositories) == 0:
            log.error(f"Fail to get repositories.")
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        tmp_repo = None
        for repo in repositories:
            if repo.get("repositoryType") == target_type:
                tmp_repo = repo
                break
        if not tmp_repo:
            log.warn(f"Fail to get type({target_type}).")
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        tmp_path_list = tmp_repo.get("path")

        # 检查任务类型接口，取不到cache仓,正常返回
        if not tmp_path_list or len(tmp_path_list) == 0:
            return ""
        tmp_path = tmp_path_list[0]
        if not tmp_path:
            log.error("Have no can available data path")
            raise ExchangeErrCodeException(log_detail=LogDetail(
                logDetail="",
                logDetailParam=[""],
                logLevel=DBLogLevel.ERROR))
        return tmp_path
